package org.systm.security.init;

import org.global.GEntity;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
import org.systm.adminuser.entity.AdminUser;
import org.systm.adminuser.entity.UserPermission;
import org.systm.adminuser.params.UserPermissionParams;
import org.systm.adminuser.service.UserPermissionService;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Configuration
public class InitSecurity implements ApplicationListener<ContextRefreshedEvent> {

    @Resource
    UserPermissionService userPermissionService;

    @Resource
    InitUser initUser;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void onApplicationEvent(ContextRefreshedEvent event) {
        ApplicationContext applicationContext = event.getApplicationContext();
        RequestMappingInfoHandlerMapping mapping
                = applicationContext.getBean(RequestMappingInfoHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> mappingInfo = mapping.getHandlerMethods();

        try {
            initPermission(mappingInfo);
            initUser.initAdmin();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 初始化表格中的权限标识数据.
     * @param mappingInfo 映射数据
     */
    @Transactional(rollbackFor = Exception.class)
    public void initPermission(Map<RequestMappingInfo, HandlerMethod> mappingInfo) throws Exception {
        Optional<List<UserPermission>> all = userPermissionService.findAll();
        List<UserPermission> store = all.orElse(new ArrayList<>());
        List<UserPermission> cache = createPermission(mappingInfo);
        // store的补集
        List<UserPermission> cacheSupple = store.stream()
                .filter((sk) -> cache.stream().noneMatch((ck) -> ck.getUrl().equals(sk.getUrl())))
                .collect(Collectors.toList());
        // cache的补集
        List<UserPermission> storeSupple = cache.stream()
                .filter((ck) -> store.stream().noneMatch((sk) -> sk.getUrl().equals(ck.getUrl())))
                .collect(Collectors.toList());
        // 并集
        List<UserPermission> union = store.stream()
                .filter((sk) -> cache.stream().anyMatch((ck) -> ck.getUrl().equals(sk.getUrl())))
                .collect(Collectors.toList());

        userPermissionService.repository().deleteAllById(cacheSupple.stream().map(GEntity::getId).collect(Collectors.toList()));
        userPermissionService.repository().saveAll(storeSupple);
        UserPermissionParams<String> update = new UserPermissionParams<>();
        update.setAll(union);
        userPermissionService.updateAll(update);
    }

    /**
     * @param mappingInfo MappingHandler映射关系。
     * @return 返回创建的权限标识对象
     */
    private List<UserPermission> createPermission(Map<RequestMappingInfo, HandlerMethod> mappingInfo) {
        List<UserPermission> userPermissions = new ArrayList<>();
        List<RequestMappingInfo> requestMappingInfos = mappingInfo.keySet().stream()
                .filter(info -> {
                    if (info.getDirectPaths().isEmpty()) return false;
                    if (info.getDirectPaths().size() > 1) throw new RuntimeException("处理器和url要一一对应");
                    Stream<MappingPermission> stream = MappingPermission.mappingList.stream();
                    return stream.anyMatch((m) -> {
                        Set<String> directPaths = info.getDirectPaths();
                        return directPaths.contains(m.getUrl());
                    });
                })
                .collect(Collectors.toList());
        for (RequestMappingInfo info : requestMappingInfos) {
            UserPermission userPermission = new UserPermission();
            userPermission.setMethod(
                    info.getMethodsCondition()
                            .getMethods()
                            .toString()
                            .replace("[", "")
                            .replace("]", "")
            );
            userPermission.setUrl(
                    info.getDirectPaths()
                            .toString()
                            .replace("[", "")
                            .replace("]", "")
            );
            MappingPermission mappingPermission = MappingPermission.mappingMap.get(userPermission.getUrl());
            userPermission.setSpec(mappingPermission.getSpec());
            userPermission.setPer(mappingPermission.getPermission());
            userPermissions.add(userPermission);
        }
        return userPermissions;
    }
}
